package com.slunk.platforms;

import com.slunk.platforms.SAction;
//import com.slunk.platforms.SDialogueDecision.SLabel;
import com.slunk.platforms.SLabel;
import com.slunk.accessors.SDialogueAccessor;
import com.slunk.interfaces.STargetedObjectInterface;
//import com.slunk.platforms.SDialogueDecision;
import aurelienribon.tweenengine.BaseTween;
import aurelienribon.tweenengine.Tween;
import aurelienribon.tweenengine.TweenCallback;
import aurelienribon.tweenengine.TweenManager;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont.TextBounds;
import com.badlogic.gdx.graphics.g2d.NinePatch;
import com.badlogic.gdx.graphics.g2d.Sprite;
//import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
//import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import java.util.Timer;  
import java.util.TimerTask; 

public class SDialogue extends SRenderableNode implements STargetedObjectInterface, SLabelEvents {
	//The dialogue box
	private NinePatch dialoguePatch;
	//The coordinate position of the dialogue box
	private float x_pos;
	private float y_pos;
	//The the dimensions of the dialogue box
	private float width;
	private float height;
	//Default height of the dialogue box
	private float defaultHeight;
	private float x_text_pos;
	private float y_text_pos;
	private TweenManager parentTweenManager;
	private SAction onShowAction;
	private SAction onDismissAction;
	private boolean isDisplayed = false;
	private boolean displayText = false;
	private BitmapFont bitmapFont;
	private String textString;
	private float paddingWidth = -25;
	private float paddingHeight = 25;
	private Array<String> stringArray;
	private int dialogueIndex = 0;
	private SDialogueEvents eventHandler;
	private Object data;
	
	//Just for asking questions :(
	//private boolean containsQuestion = false;
	//private SDialogueDecision posedQuestion;
	//private String posedQuestionString;
	//private String[] posedAnswers;
	private boolean isQuestion = false;
	SLabel posedQuestion;
	private Array<SLabelHighlight> decisions;

	//for crawl stuffs
	public boolean isCrawlEnabled = false;
	public boolean isCrawling = false;
	public long crawlSpeed = 20; //this is in miliseconds. 1000 miliseconds is 1 second
									//fast = 10
									//medium = 25
									//slow = 40
	int crawlIndex;
	private String crawlText;
	private Timer crawlTimer;
	
	public SDialogue(NinePatch patch, String fntFilePath, String fntImagePath, float width, float height) {
		this.width = width;
		setHeight(height + paddingHeight);
		this.defaultHeight = this.height;
		this.dialoguePatch = patch;
	
		decisions = new Array<SLabelHighlight>();
		
		if (fntFilePath == null || fntImagePath == null) {
			bitmapFont = new BitmapFont();
		} else {
			bitmapFont = new BitmapFont(Gdx.files.internal(fntFilePath),Gdx.files.internal(fntImagePath),false);
		}
		this.setPosition(0, 0);
		this.stringArray = new Array<String>();
		
		SAction showAction = new SAction();
		showAction.setDuration(1.0f);
		showAction.setType(SDialogueAccessor.FADE);
		float[] onShowArr;
		onShowArr = new float[1];
		onShowArr[0] = 1.0f;
		showAction.setArgs(onShowArr);
		onShowAction = showAction;
		
		SAction dismissAction = new SAction();
		dismissAction.setDuration(1.0f);
		dismissAction.setType(SDialogueAccessor.FADE);
		float[] onDisArr;
		onDisArr = new float[1];
		onDisArr[0] = 0.0f;
		dismissAction.setArgs(onDisArr);
		onDismissAction = dismissAction;
		
		data = null;
	}
	public SDialogue(NinePatch patch, float width, float height) {
		this(patch,null,null,width,height);
	}
	public SDialogue(String backgroundIamge, float width, float height, int left, int right, int top, int bottom, String fntFilePath, String fntImagePath) {	
		this(new NinePatch(new Texture(Gdx.files.internal(backgroundIamge)), left, right, top, bottom),fntFilePath,fntImagePath,width,height);
	}
	public SDialogue(String backgroundImage, float width, float height, int left, int right, int top, int bottom) {
		this(new NinePatch(new Texture(Gdx.files.internal(backgroundImage)), left, right, top, bottom), null, null, width, height);
	}

	/**
	 * Returns the absolute height of the dialogue box, including the padding
	 * @return
	 */
	public float getHeight() {
		return this.height + paddingHeight;
	}
	/**
	 * Sets the height without the padding
	 * @param height
	 */
	public void setHeight(float height) {
		this.height = height;
	}
	/**
	 * Returns the absolute width of the dialogue box, adding the padding
	 * @return
	 */
	public float getWidth() {
		return this.width + paddingWidth;
	}
	/**
	 * Sets the width without the padding
	 * @param width
	 */
	public void setWidth(float width) {
		this.width = width;
	}
	
	public void setData(Object data) {
		this.data = data;
	}
	public Object getData() {
		return data;
	}
	
	public void setEventHandler(SDialogueEvents eventHandler) {
		this.eventHandler = eventHandler;
	}
	public SDialogueEvents getEventHandler() {
		return eventHandler;
	}
	
	//set methods
	public void setPosition(float x_pos, float y_pos) {
		this.x_pos = x_pos;
		this.y_pos = y_pos;
	}
	
	public void setDecisionText(String textString, String[] answers) {
		isQuestion = true;
		stringArray.clear();
		dialogueIndex = 0;
		decisions.clear();
		float totalHeight = 0;
		float maxHeight = this.defaultHeight;
		float sampleHeight = 0;
		
		//the posed question create first
		posedQuestion = new SLabel(textString,0.0f,0.0f,this.getWidth());
		posedQuestion.setEventHandler(this);
		posedQuestion.crawlLabel(0, 1.0f, .03f);
		TextBounds bounds = posedQuestion.getBounds();
		
		//add to total height
		sampleHeight = totalHeight = totalHeight + bounds.height;

		//Add other labels, the choices the player can choose
		//Sprite selectedBox = new Sprite(new Texture(Gdx.files.internal("data/pnSelectedBox.png")));
		for (int i = 0; i < answers.length; i++) {
			SLabelHighlight choiceObject = new SLabelHighlight(answers[i],0.0f,0.0f,this.getWidth(), new Sprite(new Texture(Gdx.files.internal("data/pnSelectedBox.png"))));
			//choiceObject.crawlLabel(0, 1.0f, .03f);
			totalHeight = totalHeight + choiceObject.height;
			if (choiceObject.height > sampleHeight) {
				sampleHeight = choiceObject.height;
			}
			decisions.add(choiceObject);
		}

		//calculate the height of the dialogue box first
		if (maxHeight > totalHeight) {
			setHeight(maxHeight + paddingHeight);
		} else {
			setHeight(totalHeight + paddingHeight);
		}
		
		float heightDivider = this.height / (1 + decisions.size);
		//System.out.println(heightDivider);
		
		Rectangle dbbox = getDialogueBoundingBox();
		Vector2 dialogueCenter = this.getCenterPosition();
		
		float x = dialogueCenter.x - (bounds.width / 2);
		float y = dbbox.y + dbbox.height - (bounds.height / 2);
		posedQuestion.setPosition(x, y);
		
		//int i = 1;
		for (SLabel aLabel : decisions) {
			TextBounds labelBounds = aLabel.getBounds();
			float label_x = dialogueCenter.x - (labelBounds.width / 2);
			y = y - heightDivider +  (labelBounds.height / 2);
			aLabel.setPosition(label_x, y);
		}
	}
	
	private float getTextHeight(BitmapFont bitmap, String textString) {
		TextBounds bounds = bitmap.getWrappedBounds(textString, width);
		return bounds.height;
	}
	
	public void setText(String textString, String delimitter) {
		//clear the old dialogue box
		stringArray.clear();
		dialogueIndex = 0;
		float maxHeight = this.defaultHeight;
		String[] pieces = textString.split(delimitter);
		for (int i = 0; i < pieces.length; i++) {
			stringArray.add(pieces[i]);
			float height = getTextHeight(bitmapFont,pieces[i]);
			if (height > maxHeight) {
				maxHeight = height;
			}
		}
		setHeight(maxHeight + paddingHeight);
		this.setTextStringPosition();
		if (isCrawlEnabled) {
			this.setUpCrawlForText();
		}
	}
	
	private void setUpCrawlForText() {
		crawlIndex = 0;
		isCrawling = true;
		crawlText = stringArray.get(dialogueIndex);
		textString = "";
		crawlTimer = new Timer();
		crawlTimer.schedule(new TimerTask() {  
            @Override  
            public void run() {  
            	textString = textString + crawlText.charAt(crawlIndex);
            	if (crawlIndex < crawlText.length() - 1) {
            		crawlIndex = crawlIndex + 1;
            	} else {
            		crawlTimer.cancel();
            		isCrawling = false;
            	}
            }  
        }, 1000, crawlSpeed);
	}
	
	public Vector2 getCenterPosition() {
		float x_position = (x_pos + width) / 2;
		float y_position = (y_pos + height) / 2; 
		return new Vector2(x_position, y_position);
	}
	
	/*
	public void setIsDisplayed(boolean isDisplayed, boolean runAction) {
		//implement me!
		//if (!isDisplayed) {
		//	dismissDialogue();
		//} 
	}
	*/
	
	public boolean getIsDisplayed() {
		return isDisplayed;
	}
	
	public NinePatch getDialoguePatch() {
		return dialoguePatch;
	}
	
	public void setParentTweenManager(TweenManager parentTweenManager) {
		this.parentTweenManager = parentTweenManager;
	}
	
	public TweenManager getTweenManager() {
		return parentTweenManager;
	}
	
	public void setOnShowAction(SAction onShowAction) {
		this.onShowAction = onShowAction;
	}
	
	public SAction getOnShowAction() {
		return onShowAction;
	}
	
	public void setOnDismissAction(SAction onDismissAction) {
		this.onDismissAction = onDismissAction;
	}
	
	public SAction getOnDimissAction() {
		return onDismissAction;
	}
	
	public void run(SAction action, TweenCallback callback) {
		if (callback != null) {
			Tween.to(this, action.getType(), action.getDuration()).target(action.getArgs()).setCallback(callback).start(parentTweenManager);
		} else {
			Tween.to(this, action.getType(), action.getDuration()).target(action.getArgs()).start(parentTweenManager);
		}
	}
	
	public void showDialogue() {
		this.cancelActions();
		isDisplayed = true;
		if (onShowAction != null) {
			this.run(onShowAction,showAnimationFinished);
		} else {
			Color colorCopy = dialoguePatch.getColor().cpy();
			Color transformedColor = new Color(colorCopy.r,colorCopy.g,colorCopy.b,1.0f);
			dialoguePatch.setColor(transformedColor);
		}
	}
	
	public void dismissDialogue() {
		this.cancelActions();
		//lets halt this until the animation has been completed.
		displayText = false;
		stringArray.clear();
		posedQuestion = null;
		//posedAnswers = null;
		decisions.clear();
		isCrawling = false;
		data = null;
		isQuestion = false;
		
		if (onDismissAction != null) {
			this.run(onDismissAction,dismissAnimationFinished);
		} else {
			Color colorCopy = dialoguePatch.getColor().cpy();
			Color transformedColor = new Color(colorCopy.r,colorCopy.g,colorCopy.b,0.0f);
			dialoguePatch.setColor(transformedColor);
			isDisplayed = false;
		}
	}
	
	public void advanceDialogue() {
		//for now, questions can not be page-able.
		if (isQuestion) {
			return;
		}
		
		if (dialogueIndex == stringArray.size - 1) {
			if (eventHandler != null) {
				eventHandler.onFoundEndOfString(this);
			}
		} else if (dialogueIndex < stringArray.size - 1){
			dialogueIndex = dialogueIndex + 1;
			setTextStringPosition();
			if (isCrawlEnabled) {
				this.setUpCrawlForText();
			}
		}
	}
	
	/**
	 * Methods to override
	 */
	public void drawObject(SpriteBatch batch) {
		if (isDisplayed) {
			dialoguePatch.draw(batch,x_pos,y_pos,width,height);
		}
		if ((stringArray.size > 0) && displayText && textString.length() > 0) {
			bitmapFont.drawWrapped(batch, textString, x_text_pos, y_text_pos, width - paddingWidth);
		} else if (isQuestion == true) {
			posedQuestion.drawSelf(batch);
			for (SLabel aLabel : decisions) {
				aLabel.drawSelf(batch);
			}
		}
	}
	
	public void onEnter() {
		//lets set the alpha channel to 0 so this dialogue is invisible
		Color colorCopy = dialoguePatch.getColor().cpy();
		Color transformedColor = new Color(colorCopy.r,colorCopy.g,colorCopy.b,0.0f);
		dialoguePatch.setColor(transformedColor);
	}
	
	public void onExit() {
		//do nothing right now
	}
	
	/**
	 * Privates
	 */
	private void setTextStringPosition() {
		TextBounds bounds = bitmapFont.getWrappedBounds(stringArray.get(dialogueIndex), width - paddingWidth);
		Vector2 dialogueCenter = this.getCenterPosition();
		x_text_pos = dialogueCenter.x - (bounds.width / 2);
		y_text_pos = dialogueCenter.y + (bounds.height / 2);
	}
	
	public Vector2 getTextStringPosition(BitmapFont bitmap, String text) {
		TextBounds bounds = bitmap.getWrappedBounds(text, width - paddingWidth);
		Vector2 dialogueCenter = this.getCenterPosition();
		float x = dialogueCenter.x - (bounds.width / 2);
		float y = dialogueCenter.y + (bounds.height / 2);
		return new Vector2(x,y);
	}
	
	
	//public Rectangle (float x, float y, float width, float height) {
	public Rectangle getDialogueBoundingBox() {
		return new Rectangle(this.x_pos,this.y_pos,this.width,this.height);
	}
	
	private void cancelActions() {
		parentTweenManager.killTarget(this);
	}
	
	private final TweenCallback showAnimationFinished = new TweenCallback() {
		@Override
		public void onEvent(int arg0, BaseTween<?> arg1) {
			displayText = true;
			if(eventHandler != null) {
				eventHandler.onShowAnimationFinishedWithData(data);
			}
		}
	};
	private final TweenCallback dismissAnimationFinished = new TweenCallback() {
		@Override
		public void onEvent(int arg0, BaseTween<?> arg1) {
			if (eventHandler != null) {
				isDisplayed = false;
				eventHandler.onDismissAnimationFinishedWithData(data);
			}
		}
	};
	
	@Override
	public Boolean containsTouch(float x, float y) {
		// TODO Auto-generated method stub
		if (isDisplayed) {
			Rectangle bbox = new Rectangle(x_pos,y_pos,width,height);
			return bbox.contains(x, y);
		} 
		return false;
	}
	@Override
	public Boolean touchDown(float x, float y) {
		if (isQuestion) {
			if (isStillCrawlingText()) {
				//lets not crawl the text anymore
				advanceAllCrawlingText();
			} else {
				//text has not been crawling, accept an answer
				boolean foundLabel = false;
				for (int i = 0; i < decisions.size; i++) {
					boolean touched = decisions.get(i).containsMousePosition(x, y);
					if (touched) {
						foundLabel = true;
						if (eventHandler != null) {	
							eventHandler.didAnswerWith(i, data);
						}
					}
				}
				if (!foundLabel) {
					dismissDialogue();
				}
			}
		}
		else if (this.isCrawlEnabled && isCrawling) {
			crawlTimer.cancel();
			textString = stringArray.get(dialogueIndex);
			isCrawling = false;
		} else if (this.isCrawlEnabled && !isCrawling) {
			this.advanceDialogue();
		}
		return isTouchEnabled;
	}
	
	@Override
	public Boolean touchDragged() {
		// TODO Auto-generated method stub
		return isTouchEnabled;
	}
	@Override
	public Boolean touchUp() {
		// TODO Auto-generated method stub
		return isTouchEnabled;
	}
	@Override
	public Boolean mouseMoved(float screenX, float screenY) {
		// TODO Auto-generated method stub
		if (isQuestion && isDisplayed) {
			//if a question is visible, display the highlights
			for (SLabelHighlight label : decisions) {
				label.containsMousePosition(screenX, screenY);
			}
		}
		return true;
	}
	@Override
	public int compareTo(SRenderableNode o) {
		// TODO Auto-generated method stub
		//return 0;
		return z_order - o.z_order;
	}
	
	/**
	 * Check to see if is question, if it is, check if any of the labels are crawling.
	 * @return True if the label is still crawling, otherwise return false.
	 */
	public boolean isStillCrawlingText() {
		if (posedQuestion.isCrawlingLabel()) {
			return true;
		}
		for (SLabelHighlight label : decisions) {
			if (label.isCrawlingLabel()) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Advance all the crawling text
	 */
	public void advanceAllCrawlingText() {
		posedQuestion.dismissCrawlingLabel();
		posedQuestion.displayLabel();
		for (SLabelHighlight label : decisions) {
			label.dismissCrawlingLabel();
			label.displayLabel();
		}
	}
	@Override
	public void didFinishCrawling(SLabel label) {
		// TODO Auto-generated method stub
		if (label == posedQuestion) {
			advanceAllCrawlingText();
		}
	}
}
